C++11 부터 throw()가 derpecated 되고, noexcept 키워드가 추가되었다.
noexcept는 연산자(operator)와 한정자(specifier)의 형태로 제공된다.
operator
noexcept는 컴파일 타임에 해당 표현식이 예외를 던지지 않는 표현식인지 체크하여,
true/false를 반환한다.
표현식이 아래의 경우 중 하나라도 포함하면, false를 반환한다. 그렇지 않으면 true를 반환
- 상수 표현식이 아닌 함수가 noexcept 키워드를 가지지 않는 경우
- 런타임 체크가 필요한 dynamic_cast 등의 RTTI가 포함된 경우
- typeid 표현식에 포함된 타입이 상속 관계에 있는 클래스나 구조체일 경우
#include <iostream>
#include <utility>
#include <vector>
void may_throw();
void no_thorw() noexcept;
auto lmay_throw=[](){};
auto lno_throw=[]()noexcept{};
class T{
public:
~T(){}
};
class U{
std::vector<int> v;
public:
~U(){}
};
class V{
public:
std::vector<int> v;
};
int main(void){
T t;
U u;
V v;
noexcept(may_throw());
noexcept(no_throw());
noexcept(lmay_throw());
noexcept(lno_throw());
noexcept(std::declval<T>().~T());
noexcept(std::declval<T>());
noexcept(T(t));
noexcept(U(std::declval<U>()));
noexcept(U(u));
noexcept(V(std::declval<V>()));
noexcept(V(v));
}
specifier
noexcept(expression)
noexcept
noexcept 한정자는 함수가 예외를 던질 수 있는지(false) 없는지(true)를 명시한다.
template <typename T>
void foo() noexcept(true){}
void baz() noexcept{ throw 42; }
int main(void){
foo<int>();
bar();
baz();
}
std::terminate가 호출되면, std::terminated_handler를 호출하는데
기본 handler 함수는 std::abort로 호출되면 프로그램이 종료된다.
Strong exception guarantee
#include <iostream>
#include <vector>
struct foo{
int value;
explicit foo(int value): value(value){
std::cout<<"foo("<<value<<"\n";
}
foo(const foo& other): value(other.value){
std::cout<<"foo(foo("<<value<<"))\n";
}
foo(foo&& other) noexcept: value(std::move(other.value)){
other.value=-1;
std::cout<<"foo(move(foo("<<value<<"))\n";
}
~foo(){
if(value!=-1) std::cout<<"~foo("<<value<<"\n";
}
};
int main(void){
std::vector<foo> foos;
foos.emplace_back(1);
foos.emplace_back(2);
}
C++11 이후 std::vector를 포함한 STL 컨테이너들은 move semantics가 모두 적용되어 있다.원소에 대한 이동처리를 할 때, 해당 원소가 move시 noexcept를 지원하지 않으면, move semantics가 아닌
copy semantics로 elements를 처리한다.